#include "sys_spi.h"
#include "sys_types.h"
#include "sys_uart.h"
#include "sys_interrupt.h"
#include "sys_delay.h"
#include "sys_gpio.h"
#include "sys_io.h"
#include "sys_cache.h"

//默认最大时钟频率
#define  SPI_MAX_CLK_HZ (60000000)
/*DMA使能时需要使用到内存分配，如果有使用系统,不能使用C库的malloc,请使用固定内存分配或系统的内存管理*/
#ifdef __SPI_DMA_EN //SPI DMA使能
	#define SPI_DMA_EN 1 
#endif
/*---------------------------------------------------
修改记录
2020-04-13 
	1.128x512字节写不正常，sys_spi.c中SPI_Flash_Write函数的参数unsigned short NumByteToWrite改为unsigned int NumByteToWrite
2020-08-19 
	1.增加DMA读写模式
2020-12-17 
	1.增加32K块，64K块擦除函数
	2.增加读UID(唯一ID),设备ID函数
2021-06-10 
	1.DMA与中断开启的情况读写失败：增加一个虚拟缓存virtual_buff替换读写函数的NULL代入参数。
2021-07-07
	1.virtual_buff改为使用malloc分配，如果有使用系统,不能使用C库的malloc,请使用固定内存分配或系统的内存管理。
2021-07-09
	1.增加对16MB以上型号读写的支持,主要增加进入4地址模式与4地址读写-W25Q256测试正常。
2021-07-14
	1.增加对CLK的频率限制与最大频率设置函数,避免频率太高而读写错误问题。

	
----------------------------------------------------*/


#if SPI_DMA_EN	
	#include "sys_dma.h"
	#include "stdlib.h"
	void *spi_malloc(size_t size)
	{
		void *p=malloc(size);
		if(((int)p%4)!=0)//DMA缓存需要4字节对齐
		{
			sysprintf("ERR:spi_malloc() alignment(%08x)\r\n",p);
			free(p);
			return 0;
		}
		return p;
	}
	void spi_free(void *p)
	{
		free(p);
	}	
#endif


#define spi_get_addr(x) (0x01C05000+x*0x1000)//返回SPI地址

#if SPI_DMA_EN
	DMA * SPI_TXdma;
	DMA * SPI_RXdma;
#endif

enum {
	SPI_GCR	= 0x04,
	SPI_TCR	= 0x08,
	SPI_IER	= 0x10,
	SPI_ISR	= 0x14,
	SPI_FCR	= 0x18,
	SPI_FSR	= 0x1c,
	SPI_WCR	= 0x20,
	SPI_CCR	= 0x24,
	SPI_MBC	= 0x30,
	SPI_MTC	= 0x34,
	SPI_BCC	= 0x38,
	SPI_TXD	= 0x200,
	SPI_RXD	= 0x300,
};

//HZ 最大时钟限制
int spi_max_clk=SPI_MAX_CLK_HZ;
void set_max_clk(int clk)
{
	spi_max_clk=clk;
}
/*---------------------------------------------------
SPI DMA传输时的等待->多任务时可执行其它任务
---------------------------------------------------*/
void spi_dma_delay(void)
{
	//delay_ms(1);
}
/*---------------------------------------------------
SPI配置
---------------------------------------------------*/
void spi_spi_confing(int SPI)
{
	virtual_addr_t addr=spi_get_addr(SPI);
	u32_t val;

	/* 
	if CPU_CLK=600MHZ
	AHB_CLK=CPU_CLK/2=600/2=300
	SPI_CLK=AHB_CLK / (2*(n + 1)); 
	[n=2 SPI_CLK=50MHZ] 
	[n=1 SPI_CLK=75MHZ] 
	[n=0 SPI_CLK=150MHZ] 
	*/
	
	/* 
	if CPU_CLK=408MHZ
	AHB_CLK=CPU_CLK/2=408/2=204
	SPI_CLK=AHB_CLK / (2*(n + 1)); 
	
	
  [n=5 SPI_CLK=17MHZ] 
	[n=4 SPI_CLK=20.4MHZ] 
	[n=3 SPI_CLK=25.5MHZ] 
	[n=2 SPI_CLK=34MHZ] 
	[n=1 SPI_CLK=51MHZ] 
	[n=0 SPI_CLK=102MHZ] 
	*/
	int n=0;
	int ahb_clk=get_ahb_frequency();
	while(n<10)
	{
		int spi_clk=ahb_clk / (2*(n + 1)); 
		if(spi_clk>spi_max_clk)//SPI 时钟频率限制
		{
			sysprintf("SPI%d CLK SET.\r\n",SPI,n);	
			n++;
		}else
		{
			sysprintf("SPI%d CLK[%dHZ]...\r\n",SPI,spi_clk);				
			break;		
		}
	}
	/* Set spi clock rate control register, divided by 4 */
	write32(addr + SPI_CCR, 0x00001000|n);

	/* Enable spi and do a soft reset */
	val = read32(addr + SPI_GCR);
	val |= ((u64_t)1 << 31) | (1 << 7) | (1 << 1) | (1 << 0);
	write32(addr + SPI_GCR, val);
	while(read32(addr + SPI_GCR) & ((u64_t)1 << 31));

	val = read32(addr + SPI_TCR);
	val &= ~(0x3 << 0);
	val |= (1 << 6) | (1 << 2);
	write32(addr + SPI_TCR, val);

	val = read32(addr + SPI_FCR);
	val |= ((u64_t)1 << 31) | (1 << 15);
	write32(addr + SPI_FCR, val);
}

/*---------------------------------------------------
SPI开CS
---------------------------------------------------*/
void sys_spi_select(int SPI)
{
	virtual_addr_t addr = spi_get_addr(SPI);
	u32_t val;

	val = read32(addr + SPI_TCR);
	val &= ~((0x3 << 4) | (0x1 << 7));
	val |= ((0 & 0x3) << 4) | (0x0 << 7);
	write32(addr + SPI_TCR, val);
}
/*---------------------------------------------------
SPI关CS
---------------------------------------------------*/
void sys_spi_deselect(int SPI)
{
	virtual_addr_t addr =spi_get_addr(SPI);
	u32_t val;

	val = read32(addr + SPI_TCR);
	val &= ~((0x3 << 4) | (0x1 << 7));
	val |= ((0 & 0x3) << 4) | (0x1 << 7);
	write32(addr + SPI_TCR, val);
}
/*---------------------------------------------------
SPI写
---------------------------------------------------*/
static void sys_spi_write_txbuf(int SPI,u8_t * buf, int len)
{
	virtual_addr_t addr =spi_get_addr(SPI);
	int i;

	if(!buf)
		len = 0;

	write32(addr + SPI_MTC, len & 0xffffff);
	write32(addr + SPI_BCC, len & 0xffffff);
	for(i = 0; i < len; ++i)
		write8(addr + SPI_TXD, *buf++);
}
/*---------------------------------------------------
使能DMA_DRQ
---------------------------------------------------*/
void sys_spi_enable_drq(int SPI,int tx_drq_en,int rx_drq_en)
{
	virtual_addr_t addr =spi_get_addr(SPI);
	
	if(tx_drq_en==0)C_BIT(addr+SPI_FCR,24);//TX_DRQ
	else S_BIT(addr+SPI_FCR,24);
	
	if(rx_drq_en==0)C_BIT(addr+SPI_FCR,8);//RX_DRQ
	else S_BIT(addr+SPI_FCR,8);
}
/*---------------------------------------------------
设置计数值
---------------------------------------------------*/
void sys_spi_set_count(int SPI,int count)
{
	virtual_addr_t addr =spi_get_addr(SPI);	
  write32(addr + SPI_MBC, count & 0xffffff);	
  write32(addr + SPI_MTC, count & 0xffffff);
	write32(addr + SPI_BCC, count & 0xffffff);
}
/*---------------------------------------------------
SPI开始
---------------------------------------------------*/
void sys_spi_start(int SPI)
{
	virtual_addr_t addr =spi_get_addr(SPI);	
	S_BIT(addr + SPI_TCR,31);
}
/*---------------------------------------------------
SPI SPI CPU传输
----------------------------------------------------*/
static int sys_spi_cpu_transfer(int SPI,void * txbuf, void * rxbuf, int len)
{
	virtual_addr_t addr =spi_get_addr(SPI);
	int count = len;
	u8_t * tx = txbuf;
	u8_t * rx = rxbuf;
	u8_t val;
	int n, i;
	while(count > 0)
	{
		n = (count <= 64) ? count : 64;
		write32(addr + SPI_MBC, n);
		sys_spi_write_txbuf(SPI,tx, n);
		write32(addr + SPI_TCR, read32(addr + SPI_TCR) | ((u64_t)1 << 31));

		while((read32(addr + SPI_FSR) & 0xff) < n);
		
		for(i = 0; i < n; i++)
		{
			val = read8(addr + SPI_RXD);
			if(rx)*rx++ = val;
		}
		if(tx)tx += n;
		count -= n;
	}
	return len;
}
#if SPI_DMA_EN
/*---------------------------------------------------
SPI DMA transfe
----------------------------------------------------*/
int sys_spi_dma_transfer(int SPI,DMA * TXdma,DMA * RXdma,void * txbuf, void * rxbuf, int len)
{
  virtual_addr_t addr =spi_get_addr(SPI);		
	//---------
	TXdma->Type=NDMA;//类型 
	TXdma->Ch=0;//通道 	
	TXdma->Byte_Counter=len;//Byte计数	
	TXdma->Continuous_Mode_Enable=0;//连续模式
	TXdma->Read_Byte_Counter_Enable=0;//读计数值使能		
	//---------	
	TXdma->Source_Address=(unsigned int)txbuf;//源地址	
	TXdma->Source_Address_Type=DMA_ADDRESS_TYEP_LINEER;//源地址类型
	TXdma->Source_DRQ_Type=NDMAS_DRQ_Type_SDRAM_Memory;//源类型
	TXdma->Source_Data_Width=DMA_DATA_WIDTH_8;//源数据宽度
	TXdma->Source_Burst_Length=DMA_BURST_LENGTH_1;//BURST
	//-----------	
	TXdma->Destination_Address=(unsigned int)(addr + SPI_TXD);//目标地址	
	TXdma->Destination_Address_Type=DMA_ADDRESS_TYEP_IO;//目标地址类型
	TXdma->Destination_DRQ_Type=(SPI==0?NDMAD_DRQ_Type_SPI0_Tx:NDMAD_DRQ_Type_SPI1_Tx);//目标类型
	TXdma->Destination_Data_Width=DMA_DATA_WIDTH_8;//目标数据宽度
	TXdma->Destination_Burst_Length=DMA_BURST_LENGTH_1;//BURST
	//-----------	
//	sysprintf("发送数据大小: %d \r\n",TXdma->Byte_Counter);
	DMA_Config(TXdma);
	DMA_Enable(TXdma);		
	
	//---------------------------------------------------------------------------------------
	RXdma->Type=NDMA;//类型 
	RXdma->Ch=1;//通道 	
	RXdma->Byte_Counter=len;//Byte计数	
	RXdma->Continuous_Mode_Enable=0;//连续模式
	RXdma->Read_Byte_Counter_Enable=0;//读计数值使能		
	//---------	
	RXdma->Source_Address=(unsigned int)(addr + SPI_RXD);//源地址	
	RXdma->Source_Address_Type=DMA_ADDRESS_TYEP_IO;//源地址类型
	RXdma->Source_DRQ_Type=(SPI==0?NDMAS_DRQ_Type_SPI0_Rx:NDMAS_DRQ_Type_SPI1_Rx);//源类型
	RXdma->Source_Data_Width=DMA_DATA_WIDTH_8;//源数据宽度
	RXdma->Source_Burst_Length=DMA_BURST_LENGTH_1;//BURST
	//-----------	
	RXdma->Destination_Address=(unsigned int)rxbuf;//目标地址	
	RXdma->Destination_Address_Type=DMA_ADDRESS_TYEP_LINEER;//目标地址类型
	RXdma->Destination_DRQ_Type=NDMAD_DRQ_Type_SDRAM_Memory;//目标类型
	RXdma->Destination_Data_Width=DMA_DATA_WIDTH_8;//目标数据宽度
	RXdma->Destination_Burst_Length=DMA_BURST_LENGTH_1;//BURST
	//-----------	
	//sysprintf("接收数据大小: %d \r\n",RXdma->Byte_Counter);
	DMA_Config(RXdma);
	DMA_Enable(RXdma);
	
	//设置计数值
	sys_spi_set_count(SPI,len);
	//开启DMA_DRQ
	sys_spi_enable_drq(SPI,1,1);
	//片选使能
  //sys_spi_select(SPI);
	//SPI开始
	sys_spi_start(SPI);
	//----
 // sysprintf("等待发送 \r\n");
	while(1)
	{
		if(DMA_Get_Full_TIP(RXdma))
		{
			//sysprintf("发送数据完成...\r\n");
			break;
		}
		//延时或作务
		spi_dma_delay();
	}
	DMA_Disable(RXdma);
	DMA_Disable(TXdma);
	//写通CACHE
//	sysInvalidCache();
	sysFlushCache(D_CACHE);
	//片选失能
//	sys_spi_deselect(SPI);
	//--
	return 0;
}
#endif

/*SPI传输
*/
int sys_spi_transfer(int SPI,void * txbuf, void * rxbuf, int len)
{
#if SPI_DMA_EN	
	void *p=0;
	if(len>64)//>64 DMA模式-DMA模式时缓存指针不能为0
	{
		if(txbuf==NULL)
		{
			p=spi_malloc(len);
			if(p==0)
			{
				sysprintf("ERR:spi_malloc() txbuf==0 \r\n");
				return 0;		
			}				
			txbuf=p;
		}
		if(rxbuf==NULL)
		{
			p=spi_malloc(len);
			if(p==0)
			{
				sysprintf("ERR:spi_malloc() rxbuf==0 \r\n");
				return 0;	
			}				
			rxbuf=p;
		}		
		if(((int)txbuf%4)!=0)//DMA缓存需要4字节对齐
		{
			sysprintf("ERR:spi_malloc() txbuf alignment(%08x)\r\n",txbuf);
			return 0;	
		}
		if(((int)rxbuf%4)!=0)//DMA缓存需要4字节对齐
		{
			sysprintf("ERR:spi_malloc() rxbuf alignment(%08x)\r\n",rxbuf);
			return 0;	
		}
		
		sys_spi_dma_transfer(SPI,SPI_TXdma,SPI_RXdma,txbuf,rxbuf,len);
		if(p)spi_free(p);
	}else//CPU模式
	{
#endif		
		sys_spi_cpu_transfer(SPI,txbuf,rxbuf,len);
#if SPI_DMA_EN		
	}
#endif		
	return len;
}
	
/*SPI write_then_read
*/
static int sys_spi_write_then_read(int SPI,void * txbuf, int txlen, void * rxbuf, int rxlen)
{

	if(sys_spi_transfer(SPI,txbuf, NULL, txlen) != txlen)
		return -1;
	if(sys_spi_transfer(SPI,NULL, rxbuf, rxlen) != rxlen)
		return -1;
	return 0;
}
/*SPI write_then_write
*/
static int sys_spi_write_then_write(int SPI,void * txbuf0, int txlen0, void * txbuf1, int txlen1)
{
	if(sys_spi_transfer(SPI,txbuf0, NULL, txlen0) != txlen0)
		return -1;
	if(sys_spi_transfer(SPI,txbuf1, NULL, txlen1) != txlen1)
		return -1;
	return 0;
}

/*SPI IO初始化
*/
void spi_gpio_init(int SPI)
{
int i=0;
	if(SPI==SPI0)
	{
		for(i=0;i<4;i++)
		{
			GPIO_Congif(GPIOC,GPIO_Pin_0+i,GPIO_Mode_010,GPIO_PuPd_NOPULL);
			GPIO_Multi_Driving(GPIOC,GPIO_Pin_0+i,3);//增加驱动力
		}
	}
	else if(SPI==SPI1)
	{
		for(i=0;i<4;i++)
		{
			GPIO_Congif(GPIOE,GPIO_Pin_7+i,GPIO_Mode_100,GPIO_PuPd_NOPULL);
			GPIO_Multi_Driving(GPIOE,GPIO_Pin_7+i,3);//增加驱动力
		}
	}
}
/*SPI clock_初始化
*/
void spi_clock_init(int SPI)
{
 S_BIT(0x01c202c0,(20+SPI));/* Deassert reset */
 S_BIT(0x01c20060,(20+SPI));/* Open the bus gate */	
}

/*SPI初始化
*/
void spi_flash_init(int SPI)
{
	spi_gpio_init(SPI);
	spi_clock_init(SPI);
	spi_spi_confing(SPI);
}
//--------------------------------------------------------------------------------------------------------
//--------------------------------------W25Q128读写------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------
int addr_mode=0;//地址模式-【默认0=3位地址】【1=4位地址】-大于16MB时使用4位地址
#define W25Q256_Enter_4_Addres 0xB7
#define W24Q256_Exit_4_Addres  0xE9

/***********************************************
Reg0
	//BIT7  6   5   4   3   2   1   0
	//SPR   RV  TB BP2 BP1 BP0 WEL BUSY
	//SPR:默认0,状态寄存器保护位,配合WP使用
	//TB,BP2,BP1,BP0:FLASH区域写保护设置
	//WEL:写使能锁定
	//BUSY:忙标记位(1,忙;0,空闲)
	//默认:0x00
***********************************************/
/***********************************************
Reg1
	//BIT7  6   5   4   3   2   1   0
	//SPR   RV  TB BP2 BP1 BP0 WEL BUSY
	//SPR:默认0,状态寄存器保护位,配合WP使用
	//TB,BP2,BP1,BP0:FLASH区域写保护设置
	//WEL:写使能锁定
	//BUSY:忙标记位(1,忙;0,空闲)
	//默认:0x00
***********************************************/
/***********************************************
Reg3 
	//BIT7  6   5   4   3   2   1   0
	// (R) DRV1 DRV0(R)(R) WPS ADP ADS
  //  ADS=地址模式位 默认0   1=4位地址，否则3位地址 16MB以上型号会使用
***********************************************/

//读取SPI_FLASH的状态寄存器
u8 SPI_Flash_ReadSR(int SPI,char Reg)   
{  
	u8_t addr_buff[4];
	u8 buf;
	volatile int res=0;
	addr_buff[0] = Reg;
	sys_spi_select(SPI);
	if(sys_spi_write_then_read(SPI,addr_buff, 1, (void *)&buf, 1)<0)res=-1;
	sys_spi_deselect(SPI);   
	return buf;   
}

//等待空闲
void SPI_Flash_Wait_Busy(int SPI)   
{   
	while ((SPI_Flash_ReadSR(SPI,W25X_ReadStatusReg1)&0x01)==0x01);   // 等待BUSY位清空，最低位为空闲位
}
//SPI_FLASH写使能	
//将WEL置位   
int SPI_FLASH_Write_Enable(int SPI)   
{
	u8_t addr_buff[4];
	int res=0;
	addr_buff[0] = W25X_WriteEnable;
	sys_spi_select(SPI);
	if(sys_spi_transfer(SPI,addr_buff, NULL, 1) != 1)res=-1;
	sys_spi_deselect(SPI); 		  
  return res;                               	      
} 
//SPI_FLASH写禁止	
//将WEL清零  
int SPI_FLASH_Write_Disable(int SPI)   
{  
	u8_t addr_buff[4];
	int res=0;
	addr_buff[0] = W25X_WriteDisable;
	sys_spi_select(SPI);
	if(sys_spi_transfer(SPI,addr_buff, NULL, 1) != 1)res=-1;
	sys_spi_deselect(SPI); 		  
  return res;                                                           	      
} 
/*擦除
*/
int SPI_Flash_Erase_Sector(int SPI,int addr)   
{   
	u8_t addr_buff[5];
	int buff_len=0;
	int res=0;	
	if(addr_mode==0)//3地址
	{
		addr_buff[0] = 0x20;	
		addr_buff[1] = (u8_t)(addr >> 16);
		addr_buff[2] = (u8_t)(addr >> 8);
		addr_buff[3] = (u8_t)(addr >> 0);
		buff_len=4;
	}else//4地址
	{
		addr_buff[0] = 0x21;		
		addr_buff[1] = (u8_t)(addr >> 24);
		addr_buff[2] = (u8_t)(addr >> 16);
		addr_buff[3] = (u8_t)(addr >> 8);
		addr_buff[4] = (u8_t)(addr >> 0);
		buff_len=5;		
	}
	//写使能
	if(SPI_FLASH_Write_Enable(SPI) <0)res=-1;
	SPI_Flash_Wait_Busy(SPI);
	//擦除
	sys_spi_select(SPI);
	if(sys_spi_transfer(SPI,addr_buff, NULL, buff_len) != buff_len)res=-1;
	sys_spi_deselect(SPI); 	

  SPI_Flash_Wait_Busy(SPI);
	
  return res;
}
/*擦除32K块-不支持4位地址
*/
int SPI_Flash_Erase_Block32K(int SPI,int addr)   
{   
	u8_t addr_buff[4];
	int res=0;
	if(addr_mode==1)//4地址
	{
		sysprintf("ERR:Flash_Erase_Block32K 4 addresses are not supported!!!\r\n");
		return -1;
	}
	addr_buff[0] = 0x52;
	addr_buff[1] = (u8_t)(addr >> 16);
	addr_buff[2] = (u8_t)(addr >> 8);
	addr_buff[3] = (u8_t)(addr >> 0);
	
	//写使能
	if(SPI_FLASH_Write_Enable(SPI) <0)res=-1;
	SPI_Flash_Wait_Busy(SPI);
	//擦除
	sys_spi_select(SPI);
	if(sys_spi_transfer(SPI,addr_buff, NULL, 4) != 4)res=-1;
	sys_spi_deselect(SPI); 	

  SPI_Flash_Wait_Busy(SPI);
	
  return res;
}
/*擦除64K块
*/
int SPI_Flash_Erase_Block64K(int SPI,int addr)   
{   
	u8_t addr_buff[5];
	int buff_len=0;
	int res=0;	
	if(addr_mode==0)//3地址
	{
		addr_buff[0] = 0xD8;	
		addr_buff[1] = (u8_t)(addr >> 16);
		addr_buff[2] = (u8_t)(addr >> 8);
		addr_buff[3] = (u8_t)(addr >> 0);
		buff_len=4;
	}else//4地址
	{
		addr_buff[0] = 0xDC;		
		addr_buff[1] = (u8_t)(addr >> 24);
		addr_buff[2] = (u8_t)(addr >> 16);
		addr_buff[3] = (u8_t)(addr >> 8);
		addr_buff[4] = (u8_t)(addr >> 0);
		buff_len=5;		
	}
	//写使能
	if(SPI_FLASH_Write_Enable(SPI) <0)res=-1;
	SPI_Flash_Wait_Busy(SPI);
	//擦除
	sys_spi_select(SPI);
	if(sys_spi_transfer(SPI,addr_buff, NULL, buff_len) != buff_len)res=-1;
	sys_spi_deselect(SPI); 	

  SPI_Flash_Wait_Busy(SPI);
	
  return res;
}
/*读UID 64Bit 8Byte
*/
int SPI_Flash_Read_UID(int SPI,unsigned char *rbuff)   
{   
	u8_t wbuff[5];
	int res=0;
	wbuff[0] = 0x4B;
	wbuff[1] = 0x0;
	wbuff[2] = 0x0;
	wbuff[3] = 0x0;
	wbuff[4] = 0x0;
	//命令
	sys_spi_select(SPI);
	if(sys_spi_transfer(SPI,wbuff, NULL, 5) != 5)res=-1;
	if(sys_spi_transfer(SPI,NULL, rbuff, 8) != 8)res=-1;
	sys_spi_deselect(SPI); 	

  SPI_Flash_Wait_Busy(SPI);
	
  return res;
}
//读设备ID
unsigned char spi_nor_flash_read_device_id(int SPI) 
{
u8_t addr_buff[4];
u8_t buf[4];
unsigned char device_id=0;	
	
	addr_buff[0] =0xAB;
	sys_spi_select(SPI);
	if(sys_spi_write_then_read(SPI,addr_buff, 1, &buf, 4)<0)device_id=0;
	sys_spi_deselect(SPI);
	device_id=buf[3];
	return device_id;
}

/*
进入4字节地址模式-容量读写大于16MB需要改为4字节地址模式
*/
int spi_flash_enable_4_addres(int SPI)
{ 
 volatile 	int res=0;
	u8_t addr_buff[4];
	addr_buff[0] = W25Q256_Enter_4_Addres;
	sys_spi_select(SPI);
	if(sys_spi_transfer(SPI,addr_buff, NULL, 1) != 1)res=-1;
	sys_spi_deselect(SPI);
  //判断是不是进入了4位地址模式	
	if((SPI_Flash_ReadSR(SPI,W25X_ReadStatusReg3)&0x01)==0x01)
	{
		addr_mode=1;//4字节地址模式
		//sysprintf("enable 4 addr mode!!!\r\n");
		return 0;			
	}else
	{
		sysprintf("ERR: enable 4 addr mode!!!\r\n");
		return -1;
	}	
}
/*
退出4字节地址模式-容量读写大于16MB需要改为4字节地址模式
*/
int spi_flash_disable_4_addres(int SPI)
{
 volatile 	int res=0;
	u8_t addr_buff[4];
	if(addr_mode==0)
	{
		sysprintf("ERR:Current 3 addr mode!!!\r\n");
		return -1;
	}
	addr_buff[0] = W24Q256_Exit_4_Addres;
	sys_spi_select(SPI);
	if(sys_spi_transfer(SPI,addr_buff, NULL, 1) != 1)res=-1;
	sys_spi_deselect(SPI);
  
  //判断是不是进入了4位地址模式	
	if((SPI_Flash_ReadSR(SPI,W25X_ReadStatusReg3)&0x01)==0x00)
	{
		addr_mode=0;//3字节地址模式	
		//sysprintf("disable 4 addr mode!!!\r\n");
		return 0;	  		
	}else
	{
		sysprintf("ERR: disable 4 addr mode!!!\r\n");
		return -1;
	}           
}
/*SPI读
*/
int sys_spi_flash_read(int SPI,int addr, void * buf, int count)
{
	u8_t addr_buff[5];
	int buff_len=0;
	int res=0;	
	if(addr_mode==0)//3地址
	{
		addr_buff[0] = W25X_ReadData;	
		addr_buff[1] = (u8_t)(addr >> 16);
		addr_buff[2] = (u8_t)(addr >> 8);
		addr_buff[3] = (u8_t)(addr >> 0);
		buff_len=4;
	}else//4地址
	{
		addr_buff[0] = W25X_ReadData_4ADDR;//0x13	
		addr_buff[1] = (u8_t)(addr >> 24);
		addr_buff[2] = (u8_t)(addr >> 16);
		addr_buff[3] = (u8_t)(addr >> 8);
		addr_buff[4] = (u8_t)(addr >> 0);
		buff_len=5;		
	}	
	sys_spi_select(SPI);
	if(sys_spi_write_then_read(SPI,addr_buff, buff_len, buf, count)<0)res=-1;
	sys_spi_deselect(SPI);
	return res;
}

/*SPI写
*/
int sys_spi_flash_write(int SPI,int addr, void * buf, int count)
{
	u8_t addr_buff[5];
	int buff_len=0;
	int res=0;	
	if(addr_mode==0)//3地址
	{
		addr_buff[0] = W25X_PageProgram;	
		addr_buff[1] = (u8_t)(addr >> 16);
		addr_buff[2] = (u8_t)(addr >> 8);
		addr_buff[3] = (u8_t)(addr >> 0);
		buff_len=4;
	}else//4地址
	{
		addr_buff[0] = W25X_PageProgram_4ADDR;//0x12
		addr_buff[1] = (u8_t)(addr >> 24);
		addr_buff[2] = (u8_t)(addr >> 16);
		addr_buff[3] = (u8_t)(addr >> 8);
		addr_buff[4] = (u8_t)(addr >> 0);
		buff_len=5;		
	}	
	//写使能
	if(SPI_FLASH_Write_Enable(SPI) <0)res=-1;
	SPI_Flash_Wait_Busy(SPI);
	//写
	sys_spi_select(SPI);
	if(sys_spi_write_then_write(SPI,addr_buff, buff_len, buf, count)<0)res=-1;
	sys_spi_deselect(SPI);
	SPI_Flash_Wait_Busy(SPI);
	return res;
}
//无检验写SPI FLASH 
//必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
//具有自动换页功能 
//在指定地址开始写入指定长度的数据,但是要确保地址不越界!
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大65535)
//CHECK OK
void SPI_Flash_Write_NoCheck(int SPI,u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   
{ 			 		 
	u16 pageremain;	   
	pageremain=256-WriteAddr%256; //单页剩余的字节数		 	    
	if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节
	while(1)
	{	   
		sys_spi_flash_write(SPI,WriteAddr,(void *)pBuffer,pageremain);
		if(NumByteToWrite==pageremain)break;//写入结束了
	 	else //NumByteToWrite>pageremain
		{
			pBuffer+=pageremain;				  //加上前一页的写和数量得到下页写数据的开始
			WriteAddr+=pageremain;				  //加上前一页的写的地此得到下页写地此的开始

			NumByteToWrite-=pageremain;			  //减去已经写入了的字节数
			if(NumByteToWrite>256)pageremain=256; //一次可以写入256个字节
			else pageremain=NumByteToWrite; 	  //不够256个字节了，，，，，
		}
	}	    
} 
//带擦除操作的写FLASH  
//在指定地址开始写入指定长度的数据
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大65535)  		   
__align(4) u8 SPI_FLASH_BUF[4096];
void SPI_Flash_Write(int SPI,unsigned char * pBuffer,unsigned int WriteAddr,unsigned int NumByteToWrite)   
{ 
	u32 secpos;
	u16 secoff;
	u16 secremain;	   
 	u16 i;    

	secpos=WriteAddr/4096;//扇区地址 0~511 for w25x16 ，4K一个扇区
	secoff=WriteAddr%4096;//在扇区内的偏移
	secremain=4096-secoff;//扇区空间大小   

	if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于4096个字节，小于一个扇区时得到剩余空间
	while(1) 
	{	
		//SPI_Flash_Read(SPI_FLASH_BUF,secpos*4096,4096);//读出整个扇区的内容
		sys_spi_flash_read(SPI,secpos*4096,(void *)SPI_FLASH_BUF,4096);
		for(i=0;i<secremain;i++)//校验数据
		{
			if(SPI_FLASH_BUF[secoff+i]!=0XFF)break;//判断是否需要擦除  	  
		}
			
		if(i<secremain)//需要擦除
		{
			SPI_Flash_Erase_Sector(SPI,secpos*4096);//擦除这个扇区 W25Q128要x4096
			for(i=0;i<secremain;i++)	   //复制
			{
				SPI_FLASH_BUF[i+secoff]=pBuffer[i];//把数据复制到一个扇区的剩余空间，	  
			}	
			SPI_Flash_Write_NoCheck(SPI,SPI_FLASH_BUF,secpos*4096,4096);//写入整个扇区 ，以便前面擦除的的数据能还原 

		}else SPI_Flash_Write_NoCheck(SPI,pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间. 
						   
		if(NumByteToWrite==secremain)break;//写入结束了
		else//写入未结束
		{
			secpos++;//扇区地址增1
			secoff=0;//第二次写入时，偏移位置为0 	 

		   	pBuffer+=secremain;  //指针偏移
			WriteAddr+=secremain;//写地址偏移	   
		   	NumByteToWrite-=secremain;				//字节数递减
			if(NumByteToWrite>4096)secremain=4096;	//下一个扇区还是写不完
			else secremain=NumByteToWrite;			//下一个扇区可以写完了
		}	 
	}	 	 
}
#ifdef SPI_W25Q128_TESE
/****************************************************
W25Q128_spi测试
SPI 数据小于等于64Byte时使用CPU模式，SPI 数据大于64Byte时使用DMA模式;
sys_spi_transfer里面也可改成单CPU模式或单DMA模式;
测试SPI-FLASH使用板上存储程序的SPI-FLASH，测试起始地址不能为程序区;
****************************************************/
#define TESTSIZE (8)
void W25Q128_Demo(void)
{
unsigned char id_buff[8];
int SPI=SPI0,i,s=0,addr=(1024*1024*8);
u8 buff_in[TESTSIZE];
u8 buff_out[TESTSIZE];	
#if SPI_DMA_EN	
	DMA TXdma;
	DMA RXdma;
	SPI_TXdma=&TXdma;
	SPI_RXdma=&RXdma;	
#endif		
  /*------数据初始化------*/
  for(i=0;i<TESTSIZE;i++){buff_in[i]=i&0xff;buff_out[i]=0;}
  /*------SPI初始化------*/	
	sysprintf("SPI_Demo W25Q128...\r\n");
	sysprintf("SPI%d_Init...\r\n",SPI);
  spi_flash_init(SPI);
	char did=spi_nor_flash_read_device_id(SPI); 
	if(did>0x17)//容量大于16MB，设置为4字节地址模式，设备ID=0x17为W25Q128(16MB)
	{
		spi_flash_enable_4_addres(SPI);	
		sysprintf("addr=4byte\r\n");
	}
	SPI_Flash_Read_UID(SPI0,id_buff);
	sysprintf("DID=%02X,UID:%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x\r\n",did,
		id_buff[0],id_buff[1],id_buff[2],id_buff[3],id_buff[4],id_buff[5],id_buff[6],id_buff[7]);	
#if SPI_DMA_EN	
  /*------DMA初始化------*/
	DMA_Init();	
#endif						
  /*------写入------*/
	sysprintf("SPI 写入...\r\n");
  SPI_Flash_Write(SPI,buff_in,addr,TESTSIZE); 
  /*------读出------*/
	sysprintf("SPI_读出...\r\n");
	sys_spi_flash_read(SPI,addr,buff_out,TESTSIZE);
  /*------校验------*/	
  for(i=0;i<TESTSIZE;i++)
	{
		sysprintf("read[%d]:[%d]-[%d]\r\n",i,buff_in[i],buff_out[i]);
		if(buff_in[i]==buff_out[i])s++;
	}
  if(s==TESTSIZE)sysprintf("读写正确\r\n");
	else sysprintf("读写错误\r\n");
	//退出4字节地址模式
	if(addr_mode==1)spi_flash_disable_4_addres(SPI);
	while(1);
}


#endif
